# Preprocesses a list of files with given preprocessor and preprocessor options
#
# Args:
#     preproc [in]: Preprocessor program
#     preprocopts [in]: Preprocessor options
#     srcext [in]: File extension of the source files
#     trgext [in]: File extension of the target files
#     srcfiles [in]: List of the source files
#     trgfiles [out]: Contains the list of the preprocessed files on exit
#
function(preprocess preproc preprocopts srcext trgext srcfiles trgfiles)

  set(_trgfiles)
  foreach(srcfile IN LISTS srcfiles)
    get_filename_component(filename ${srcfile} NAME)
    string(REGEX REPLACE "\\.${srcext}$" ".${trgext}" trgfile ${filename})
    add_custom_command(
      OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${trgfile}
      COMMAND ${preproc} ${preprocopts} ${CMAKE_CURRENT_SOURCE_DIR}/${srcfile} ${CMAKE_CURRENT_BINARY_DIR}/${trgfile}
      MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${srcfile})
    list(APPEND _trgfiles ${CMAKE_CURRENT_BINARY_DIR}/${trgfile})
  endforeach()
  set(${trgfiles} ${_trgfiles} PARENT_SCOPE)

endfunction()



# Preprocesses fortran files with fypp.
#
# It assumes that source files have the ".fypp" extension. Target files will be
# created with the extension ".f90". The FYPP variable must contain the path to
# the fypp-preprocessor.
#
# Args:
#     fyppopts [in]: Options to pass to fypp.
#     fyppfiles [in]: Files to be processed by fypp
#     f90files [out]: List of created f90 files on exit
#
function (fypp_f90 fyppopts fyppfiles f90files)
  preprocess("${FYPP}" "${fyppopts}" "fypp" "f90" "${fyppfiles}" _f90files)
  set(${f90files} ${_f90files} PARENT_SCOPE)
endfunction()

# For fortran sources that contain C preprocessor flags: create ".F90" files 
function (fypp_f90pp fyppopts fyppfiles F90files)
  preprocess("${FYPP}" "${fyppopts}" "fypp" "F90" "${fyppfiles}" _F90files)
  set(${F90files} ${_F90files} PARENT_SCOPE)
endfunction()

# Helper function to configure stdlib targets
#
# It preprocesses the given fypp and fypp+cpp files, combines them with the
# regular Fortran files, and creates a library target with the given name.
# Args:
#     target_name [in]: Name of the library target to create
#     regular_sources_var [in]: Regular Fortran sources
#     fypp_files_var [in]: Sources to be preprocessed with fypp
#     cpp_files_var [in]: Sources to be preprocessed with fypp and cpp
#
function(configure_stdlib_target target_name regular_sources_var fypp_files_var cpp_files_var)
    #### Pre-process: .fpp -> .f90 via Fypp
    fypp_f90("${fyppFlags}" "${${fypp_files_var}}" ${target_name}_fypp_outFiles)
    #### Pre-process: .fypp -> .F90 via Fypp (for C preprocessor directives)
    fypp_f90pp("${fyppFlags}" "${${cpp_files_var}}" ${target_name}_cpp_outFiles)

    list(APPEND all_sources ${${target_name}_fypp_outFiles})
    list(APPEND all_sources ${${target_name}_cpp_outFiles})
    list(APPEND all_sources ${${regular_sources_var}})

    add_library(${target_name} ${all_sources})
    add_library(${PROJECT_NAME}::${target_name} ALIAS ${target_name})

    set_target_properties(
      ${target_name}
      PROPERTIES
      POSITION_INDEPENDENT_CODE ON
      WINDOWS_EXPORT_ALL_SYMBOLS ON
    )

    if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 10.0)
        target_compile_options(
          ${target_name}
          PRIVATE
          $<$<COMPILE_LANGUAGE:Fortran>:-fno-range-check>
        )
    endif()

    set(LIB_MOD_DIR ${CMAKE_CURRENT_BINARY_DIR}/mod_files/${target_name}/)
    set(INSTALL_MOD_DIR "${CMAKE_INSTALL_MODULEDIR}/${target_name}")
    # We need the module directory before we finish the configure stage since the
    # build interface might resolve before the module directory is generated by CMake
    if(NOT EXISTS "${LIB_MOD_DIR}")
      file(MAKE_DIRECTORY "${LIB_MOD_DIR}")
    endif()

    set_target_properties(${target_name} PROPERTIES
        Fortran_MODULE_DIRECTORY ${LIB_MOD_DIR}
    )
    target_include_directories(${target_name} PUBLIC
        $<BUILD_INTERFACE:${LIB_MOD_DIR}>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_MODULEDIR}>
    )

    install(TARGETS ${target_name}
            EXPORT ${PROJECT_NAME}-targets
            RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
            ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
            LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
    )
    install(DIRECTORY ${LIB_MOD_DIR} DESTINATION "${INSTALL_MOD_DIR}")
endfunction()
